﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;

namespace Central.Linq.Mvc {
	/// <summary>
	/// Базовый абстрактный класс для информации о свойствах сущности.
	/// </summary>
#if !DEBUG

	[DebuggerStepThrough]
#endif

	public abstract class InfoManager {
		#region конструктор

		/// <summary>
		/// Создает базовый класс для информации по прикладным сущностям
		/// </summary>
		protected InfoManager() {
			#region инициализация переменных

			_fieldsList = new List<FieldInformation>();
			_modelInfo = new ModelInformation();

			#endregion инициализация переменных
		}

		#endregion конструктор

		#region свойства класса

		/// <summary>
		/// Возращает список описания полей сущности
		/// </summary>
		public IEnumerable<FieldInformation> FieldInformations {
			get {
				if (this._modelInfo == null || this._modelInfo.Fields.Count() == 0) {
					this._fieldsList = this.CreateFieldsList();
					if (_fieldsList != null && _fieldsList.Count > 0) {
						this._modelInfo.Fields = new Collection<FieldInformation>(this._fieldsList);
					}
				}
				return _modelInfo.Fields;
			}
		}

		/// <summary>
		/// Возращает FieldManager по имени ключа.
		/// </summary>
		/// <returns></returns>
		public FieldManager this[string key] {
			get {
				var r = getFieldManagerByKey(key).ToFieldManager();
				return r;
			}
		}

		/// <summary>
		/// Возращает FieldManager по умолчанию со всеми полями (FieldInformation) без использования ключей.
		/// </summary>
		public FieldManager DefaultFieldManager {
			get {
				var r = this.FieldInformations.ToFieldManager();
				return r;
			}
		}

		#endregion свойства класса

		#region Открытые методы класса

		/// <summary>
		/// Возвращает новый GroupManager созданный на основании коллекции FieldInformation
		/// </summary>
		/// <param name="groupByFieldsName">свойство для группировки</param>
		/// <returns></returns>
		public GroupManager CreateGroupManager(string[] groupByFieldsName) {
			return this.CreateGroupManager(groupByFieldsName, string.Empty);
		}

		/// <summary>
		/// Возвращает новый GroupManager созданный на основании коллекции FieldInformation
		/// </summary>
		/// <param name="groupByFieldsName">свойство для группировки</param>
		/// <param name="fieldNameForCount">свойства в котором отображать Count значение</param>
		/// <returns></returns>
		public GroupManager CreateGroupManager(string[] groupByFieldsName, string fieldNameForCount) {
			if (groupByFieldsName.Length == 0) throw new ArgumentNullException("groupByFieldsName");
			GroupManager result = new GroupManager();
			foreach (string item in groupByFieldsName) {
				result.Add(item);
			}
			if (!string.IsNullOrEmpty(fieldNameForCount)) {
				result.FieldNameForCount = fieldNameForCount;
			}
			foreach (FieldInformation field in this.FieldInformations) {
				if (field.DefaultAggregation.HasValue) {
					result.AddAggregateField(new AggregateFieldDefinition(field.Name, field.DefaultAggregation.Value));
				}
			}
			return result;
		}

		#endregion Открытые методы класса

		#region Австрактные методы класса

		/// <summary>
		/// создается список ModelInfo
		/// </summary>
		protected abstract List<FieldInformation> CreateFieldsList();

		#endregion Австрактные методы класса

		#region скрытые методы помощники

		// выбирает из списка полей свойства сущности по ключу
		private List<FieldInformation> getFieldManagerByKey(string key) {
			List<FieldInformation> result = new List<FieldInformation>();
			var items = this.FieldInformations.Where(x => x.HasFieldManagerKey(key));
			if (items.Any()) {
				result.AddRange(items); return result;
			}
			else {
				throw new ArgumentException("Did not match any of the field with the key name", key);
			}
		}

		#endregion скрытые методы помощники

		#region скрытые переменные

		private List<FieldInformation> _fieldsList;
		private readonly ModelInformation _modelInfo;

		#endregion скрытые переменные
	}
}